﻿using System;
using LukasKopenec.LocalizationSupport;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel
{
    /// <summary>
    /// An undoable command for inserting a new durational symbol into a composition.
    /// </summary>
    /// <remarks>
    /// Input Parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     <see cref="Beat"/> (<see cref="UInt16"/>)
    /// </term>
    /// <description>
    ///     The beat at which the symbol is inserted.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="CustomData"/> (<see cref="Array"/> of <see cref="Model.Elements.CustomSymbolData"/>)
    /// </term>
    /// <description>
    ///     The collection of custom data that will be added to the inserted symbol.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Duration"/> (<see cref="UInt16"/>)
    /// </term>
    /// <description>
    ///     The duration of the inserted symbol.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Measure"/> (<see cref="Model.Elements.MeasureElement"/>)
    /// </term>
    /// <description>
    ///     The measure to which the symbol is inserted.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Part"/> (<see cref="Model.Elements.PartDescriptorElement"/>)
    /// </term>
    /// <description>
    ///     The part into which the symbol is inserted.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Voice"/> (<see cref="Byte"/>
    /// </term>
    /// <description>
    ///     The voice into which the symbol is inserted
    /// </description>
    /// </item>
    /// </list>
    /// Output Parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     <see cref="CreatedSymbol"/> (<typeparamref name="T"/>)
    /// </term>
    /// <description>
    ///     The symbol created by the command.
    /// </description>
    /// </item>
    /// </list>
    /// </remarks>
    /// <typeparam name="T">
    /// The type of the inserted symbol. 
    /// It may be either <see cref="Model.Elements.ChordElement"/> or <see cref="Model.Elements.RestElement"/>.
    /// </typeparam>
    public class InsertDurationalSymbolCommand<T> : UndoableCommandBase
        where T : Model.Elements.DurationalSymbolElement
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="InsertDurationalSymbolCommand{T}"/> instance.
        /// </summary>
        /// <param name="undoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The command stack where the command will be put after its execution.
        /// </para>
        /// </param>
        /// <param name="redoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The command stack where the command will be put when it has been undone.
        /// </para>
        /// </param>
        public InsertDurationalSymbolCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<ushort>("Beat", ParameterDirection.In);
            AddParameter<Model.Elements.CustomSymbolData[]>("CustomData", ParameterDirection.In);
            AddParameter<ushort>("Duration", ParameterDirection.In);
            AddParameter<Model.Elements.MeasureElement>("Measure", ParameterDirection.In);
            AddParameter<Model.Elements.PartDescriptorElement>("Part", ParameterDirection.In);
            AddParameter<byte>("Voice", ParameterDirection.In);

            AddParameter<T>("CreatedSymbol", ParameterDirection.Out);

            Name = (string)LocalizationManager.GetValue("Commands.Names.InsertSymbol");
        }

        #endregion

        #region Command Logic

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            if (typeof(T) == typeof(Model.Elements.RestElement))
                CreatedSymbol = Measure.AddRest(Part, Voice, Beat, Duration, CustomData) as T;
            else
                CreatedSymbol = Measure.AddChord(Part, Voice, Beat, Duration, CustomData) as T;

            Measure.PackagePart.Package.IsModified = true;
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            CreatedSymbol.FindOwner<Model.Elements.MusicalContentElement>().AddExistingSymbol(Beat, CreatedSymbol);
            Measure.PackagePart.Package.IsModified = true;
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            CreatedSymbol.FindOwner<Model.Elements.MusicalContentElement>().RemoveSymbol(CreatedSymbol);
            Measure.PackagePart.Package.IsModified = true;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the beat at which the symbol is inserted.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The beat at which the symbol is inserted.
        /// It is given as a multiple of 128-th beats
        /// (e.g. 1st beat is 0, 2nd beat is 32, etc.).<br/>
        /// This property wraps the Beat command parameter.
        /// </para>
        /// </value>
        public ushort Beat
        {
            get { return GetParameterValue<ushort>("Beat"); }
            set { SetParameterValue<ushort>("Beat", value); }
        }

        /// <summary>
        /// Gets the symbol created by this command.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <typeparamref name="T"/>
        /// </para>
        /// <para>
        /// The symbol created by this command.
        /// </para>
        /// </value>
        public T CreatedSymbol
        {
            get { return GetParameterValue<T>("CreatedSymbol"); }
            private set { SetParameterValue<T>("CreatedSymbol", value); }
        }

        /// <summary>
        /// Gets or sets the collection of custom data that will be added to the symbol.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Array"/> of <see cref="Model.Elements.CustomSymbolData"/>
        /// </para>
        /// <para>
        /// The collection of custom data that will be associated with this symbol.<br/>
        /// This property wraps the "CustomData" command parameter.
        /// </para>
        /// </value>
        public Model.Elements.CustomSymbolData[] CustomData
        {
            get { return GetParameterValue<Model.Elements.CustomSymbolData[]>("CustomData"); }
            set { SetParameterValue<Model.Elements.CustomSymbolData[]>("CustomData", value); }
        }

        /// <summary>
        /// Gets or sets the duration of the inserted symbol.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The duration of the inserted symbol.
        /// It is given as a multiple of 128-th beats (e.g. Quarter Note = 32 128-th beats).<br/>
        /// This property wraps the "Duration" command parameter.
        /// </para>
        /// </value>
        public ushort Duration
        {
            get { return GetParameterValue<ushort>("Duration"); }
            set { SetParameterValue<ushort>("Duration", value); }
        }

        /// <summary>
        /// Gets or sets the measure to which the symol is inserted.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Model.Elements.MeasureElement"/>
        /// </para>
        /// <para>
        /// The measure to which the symbol is inserted.<br/>
        /// This property wraps the "Measure" command parameter.
        /// </para>
        /// </value>
        public Model.Elements.MeasureElement Measure
        {
            get { return GetParameterValue<Model.Elements.MeasureElement>("Measure"); }
            set { SetParameterValue<Model.Elements.MeasureElement>("Measure", value); }
        }

        /// <summary>
        /// Gets or sets the descriptor of the part to which the symbol is inserted.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Model.Elements.PartDescriptorElement"/>
        /// </para>
        /// <para>
        /// The descriptor of the part to which the symbol is inserted.<br/>
        /// This property wraps the "Part" command parameter.
        /// </para>
        /// </value>
        public Model.Elements.PartDescriptorElement Part
        {
            get { return GetParameterValue<Model.Elements.PartDescriptorElement>("Part"); }
            set { SetParameterValue<Model.Elements.PartDescriptorElement>("Part", value); }
        }

        /// <summary>
        /// Gets or sets the voice into which the symbol is inserted.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Byte"/>
        /// </para>
        /// <para>
        /// The number of the voice into which the symbol is inserted.<br/>
        /// This property wraps the "Voice" command parameter.
        /// </para>
        /// </value>
        public byte Voice
        {
            get { return GetParameterValue<byte>("Voice"); }
            set { SetParameterValue<byte>("Voice", value); }
        }

        #endregion
    }
}
